package models.games;

import java.util.Random;
import java.util.Scanner;

import models.util.Indexes;
import core.User;
import core.constants.Constants;

/**
 * In questo gioco viene mostrato all'utente una valigia aperta con almeno un
 * oggetto dentro. Lo scopo del gioco è quello di inserire un oggetto in modo
 * che non crei conflitti con quelli già presenti. Sono usate due matrici
 * boolean 'cover' e 'bag' per indicare il vano superiore (cover) e quello
 * inferiore (bag).
 * 
 * @author Stefano
 * 
 */
public class SchiacciaArancia extends Game {

	public boolean[][] cover;
	public boolean[][] bag;
	private int dimX = 2;
	private int dimY = 2;
	private int trueTilesB = 1; // situazione iniziale 'bag'
	private int trueTilesC;
	private Random random = new Random();
	private boolean increment; // serve ad alternare l'aumento delle dimensioni
								// delle matrici
	private Indexes[] trueTilesIndexesB;
	private Indexes[] trueTilesIndexesC;
	private int bagCount;

	/**
	 * Costruttore. Inizialmente è stabilita la dimensione iniziale dei vani
	 * della valigia a matrici boolean 2x2.
	 * 
	 * @param user
	 */
	public SchiacciaArancia(User user) {
		super(user);
		cover = new boolean[dimY][dimX];
		bag = new boolean[dimY][dimX];
		initialize();
	}

	/**
	 * Inizializza la situazione corrente della valigia. Sono usati array di
	 * Indexes. # cartesianProduct: contiene tutti gli indici possibili nelle
	 * matrici correnti; # trueTilesIndexesB e trueTilesIndexesC: contengono
	 * rispettivamente le celle di 'bag' e 'cover' che contengono già un
	 * oggetto. La presenza di un oggetto è indicata dalla cella posta a true
	 */
	private void initialize() {
		int dim = dimX * dimY;
		Indexes[] cartesianProductB = new Indexes[dim];
		Indexes[] cartesianProductC = new Indexes[dim];
		int nB = 0;
		int nC = 0;

		for (int i = 0; i < dimY; i++)
			for (int j = 0; j < dimX; j++) {
				cartesianProductB[nB++] = new Indexes(i, j);
				cartesianProductC[nC++] = new Indexes(i, j);
			}
		trueTilesIndexesB = new Indexes[trueTilesB];
		trueTilesIndexesC = new Indexes[trueTilesC];
		// calcola con un random quali celle di 'bag' saranno true
		for (int i = 0; i < trueTilesB; i++) {
			int x = random.nextInt(dim);
			trueTilesIndexesB[i] = cartesianProductB[x];
			cartesianProductB[x] = cartesianProductB[dim - 1];
			cartesianProductB[dim - 1] = null;
			dim--;
		}
		for (int k = 0; k < trueTilesB; k++)
			bag[trueTilesIndexesB[k].getI()][trueTilesIndexesB[k].getJ()] = true;

		dim = dimX * dimY;
		// calcola con un random quali celle di 'cover' saranno true
		for (int i = 0; i < trueTilesC && cartesianProductB[0] != null; i++) {
			int x = random.nextInt(dim);
			if (!verify(cartesianProductC[x].getI(),
					cartesianProductC[x].getJ())) {
				i--;
				cartesianProductC[x] = cartesianProductC[dim - 1];
				cartesianProductC[dim - 1] = null;
				dim--;
				continue;
			}
			trueTilesIndexesC[i] = cartesianProductC[x];
			cartesianProductC[x] = cartesianProductC[dim - 1];
			cartesianProductC[dim - 1] = null;
			dim--;
		}

		for (int k = 0; k < trueTilesC; k++)
			cover[trueTilesIndexesC[k].getI()][trueTilesIndexesC[k].getJ()] = true;
	}

	/**
	 * Gestisce la sessione di gioco e le interazioni con l'utente che,
	 * visualizzata la valigia aperta deve inserire (a riga di comando indicando
	 * riga e colonna) l'oggetto in una posizione opportuna
	 * 
	 * @param matchesCount
	 */
	public void startGame(int matchesCount) {
		Scanner sc = new Scanner(System.in);
		int count = 1;
		int inputRiga, inputColonna;
		while (count <= matchesCount) {
			printGame();
			System.out.println("Dove inserire l'oggetto?");
			System.out.print("Riga:");
			inputRiga = sc.nextInt();
			System.out.print("Colonna:");
			inputColonna = sc.nextInt();
			System.out.println();
			if (inputRiga >= dimY || inputColonna >= dimX || dimY < 0
					|| dimX < 0) {
				System.out.println("Indice fuori dai limiti");
				continue;
			}
			if (!verify(inputRiga, inputColonna)) {
				System.out.println("Errore. La valigia non si chiude");
				increment = false;
			} else {
				System.out.println("Corretto. La valigia si è chiusa.");
				incrementScore();
			}
			if (count < matchesCount)
				System.out.println("Score:" + score);
			System.out.println();
			refresh(increment);
			count++;
		}
		System.out.println("Fine RiempiValigia \nPunteggio finale: " + score);
	}

	/**
	 * Verifica se l'oggetto è stato inserito correttamente
	 * 
	 * @param i
	 * @param j
	 * @return
	 */
	public boolean verify(int i, int j) {
		if (cover[i][j] || bag[bag.length - 1 - i][j]) {
			return false; // 0-1 1-1
		}
		return true;
	}

	/**
	 * Metodo che ricostruisce le matrici 'bag' e 'cover' calcolando mediante
	 * metodi ausiliari la nuova dimensione e il numero di oggetti che avranno
	 * nella partita seguente. Infine è invocato il metodo initialize
	 */
	public void refresh(boolean flag) {
		if (increment && flag) {
			dimX = getDimensionX(dimX);
			dimY = getDimensionY(dimY);
			increment = false;
			incrementLevel();
		} else
			increment = true;
		cover = new boolean[dimY][dimX];
		bag = new boolean[dimY][dimX];
		trueTilesB = getTrueTilesB();
		trueTilesC = getTrueTilesC();
		initialize();
	}

	public int getTrueTilesB() {
		return (dimX * dimY) / 2;
		// return random.nextInt((dimX*dimY/2) - dimX*dimY/3) + (dimX*dimY)/4;
	}

	public int getTrueTilesC() {
		return (dimX * dimY) / 3;
		// return random.nextInt((dimX*dimY/2+1) - dimX*dimY/3) + dimX*dimY/4;
	}

	public Indexes[] trueTilesBag() {
		return trueTilesIndexesB;
	}

	public Indexes[] trueTilesCover() {
		return trueTilesIndexesC;
	}

	public int getDimensionX(int dimX) {
		if (dimX == 7)
			return dimX;
		return dimX + 1;
	}

	public int getDimensionY(int dimY) {
		if (dimY == 4)
			return dimY;
		if (dimX % 2 == 0)
			return dimY + 1;
		return dimY;
	}

	public void incrementScore() {
		score = score + (level + 1) * 250;
	}

	public int getDimX() {
		return dimX;
	}

	public int getDimY() {
		return dimY;
	}

	public int getBagCount() {
		return bagCount;
	}

	public void incrementBagCount() {
		bagCount++;
	}

	private void printGame() {
		// stampa cover
		for (int i = 0; i < dimY; i++) {
			for (int j = 0; j < dimX; j++) {
				if (cover[i][j]) {
					System.out.print("[X]");
				} else
					System.out.print("[ ]");
				if (i == 0 && j == dimX - 1)
					System.out.print("    COVER");
			}
			System.out.println();
		}
		System.out.println();
		// stampa bag
		for (int i = 0; i < dimY; i++) {
			for (int j = 0; j < dimX; j++) {
				if (bag[i][j])
					System.out.print("[X]");
				else
					System.out.print("[ ]");
				if (i == 0 && j == dimX - 1)
					System.out.print("    BAG");
			}
			System.out.println();
		}
	}// printGame

	public boolean[][] getCoverMatrix() {
		return cover;
	}

	public boolean[][] getBagMatrix() {
		return bag;
	}

	@Override
	public int getGameKey() {
		return 2;
	}

}
